//#define EIGEN_USE_MKL_ALL
#define EIGEN_VECTORIZE_SSE4_2
//#define EIGEN_VECTORIZE_AVX512

#include <iostream>
#include "Eigen/Core"
#include <ctime>
#include <fstream>
#include <vector>


using namespace Eigen;
using namespace std;


class Clock {
    clock_t  Begin, End;
public:
    void start() {
        this->Begin = clock();
    }
    void stop() {
        this->End = clock();
    }
    double elapseTime() {
        return double(End - Begin);
    }
};

#define VEC_CNT 300

int main() {
    ofstream ofs;
    Clock clock;
    Matrix<float, Dynamic, 32, RowMajor> x = MatrixXf::Random(VEC_CNT, 32);
//    Matrix<float, Dynamic, 32> x = MatrixXf::Random(VEC_CNT, 32);
    MatrixXf a = x.row(0);

    int max_test_size = 10000;

    float scores[VEC_CNT][VEC_CNT];



    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 第一种写法：全部向量放在一个大矩阵中，两两相乘时从大矩阵中取出
    // RowMajor对这种写法会快非常多
    clock.start();
    for (int j = 0; j < max_test_size; j++) {
        for (int row = 0; row < VEC_CNT; row++) {
            for (int col = row+1; col < VEC_CNT; col++) {
                scores[row][col] = (x.row(row) * x.row(col).transpose())(0, 0);
            }
        }
    }
    clock.stop();
    cout << "tick=" << clock.elapseTime() << endl;

    ofs.open("scores.txt",ios::out);
    for (int row = 0; row < VEC_CNT; row++) {
        for (int col = 0; col < VEC_CNT; col++) {
            ofs << scores[row][col] << endl;
        }
    }
    ofs.close();




    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 第二种写法：一个向量放在一个matrix里，这些matrix再放到一个vector里，取向量的性能会比从大矩阵中切出来更快
    vector<Matrix<float, 1, 32, RowMajor>> vx;
    for (int i = 0; i < VEC_CNT; i++) {
        vx.emplace_back(MatrixXf::Random(1, 32));
    }

    clock.start();
    for (int j = 0; j < max_test_size; j++) {
        for (int row = 0; row < VEC_CNT; row++) {
            for (int col = row+1; col < VEC_CNT; col++) {
                scores[row][col] = (vx[row] * vx[col].transpose())(0, 0);
            }
        }
    }
    clock.stop();
    cout << "tick=" << clock.elapseTime() << endl;

    ofs.open("scores2.txt",ios::out);
    for (int row = 0; row < VEC_CNT; row++) {
        for (int col = 0; col < VEC_CNT; col++) {
            ofs << scores[row][col] << endl;
        }
    }
    ofs.close();







    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 第三种写法：大矩阵和自己的转置相乘

    clock.start();
    Matrix<float, Dynamic, Dynamic, RowMajor> c;
    for (int j = 0; j < max_test_size; j++) {
        c.noalias() = x * x.transpose();
//        c = x * x.transpose();
//        x * x.transpose();
//        double b = (x * x.transpose())(0, 0);
    }
    clock.stop();
    cout << "tick=" << clock.elapseTime() << endl;

    ofs.open("scores1.txt",ios::out);
    for (int row = 0; row < VEC_CNT; row++) {
        for (int col = 0; col < VEC_CNT; col++) {
            ofs << c(row, col) << endl;
        }
    }
    ofs.close();


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 第四种写法：不用eigen
    clock.start();
    for (int j = 0; j < max_test_size; j++) {
        for (int row = 0; row < VEC_CNT; row++) {
            for (int col = 0; col < VEC_CNT; col++) {
                float r = 0;
                for (int k = 0; k < 32; k++) {
                    r += x(row, k) * x(col, k);
                }
                scores[row][col] = r;
            }
        }
    }
    clock.stop();
    cout << "tick=" << clock.elapseTime() << endl;

    ofs.open("scores1.txt",ios::out);
    for (int row = 0; row < VEC_CNT; row++) {
        for (int col = 0; col < VEC_CNT; col++) {
            ofs << scores[row][col] << endl;
        }
    }
    ofs.close();







    return 0;
}
